/*
 * Powered By XY
 * Since 2020
 */

package com.xy.biz.ts.manager.impl;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import com.xy.biz.fd.client.DocumentClient;
import com.xy.biz.fd.domain.resp.GetDocumentResponse;
import com.xy.biz.ts.dao.*;
import com.xy.biz.ts.domain.bo.*;
import com.xy.biz.ts.domain.converter.*;
import com.xy.biz.ts.domain.query.*;
import com.xy.lang.util.ResultUtils;
import com.xy.tool.util.DateUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.xy.biz.ts.manager.TrainUserManager;
import org.springframework.util.CollectionUtils;

/**
 *
 *
 * @author wwj
 * @date 2021-4-1
 */
@Service
public class TrainUserManagerImpl implements TrainUserManager {

    @Autowired
    private TrainUserDao trainUserDao;
    @Autowired
    private LearnRecordDao learnRecordDao;
    @Autowired
    private ExaminationRecordDao examinationRecordDao;
    @Autowired
    private PracticeRecordDao practiceRecordDao;
    @Autowired
    private TrainDocumentRelDao trainDocumentRelDao;
    @Autowired
    private DocumentClient documentClient;

    @Override
    public int saveTrainUser(TrainUserBO trainUserBO) {
        return trainUserDao.saveTrainUser(TrainUserConverter.toPO(trainUserBO));
    }

    @Override
    public int saveAllTrainUser(Collection<TrainUserBO> collection) {
        return trainUserDao.saveAllTrainUser(TrainUserConverter.toPO(collection));
    }

    @Override
    public int updateTrainUser(TrainUserBO trainUserBO) {
        return trainUserDao.updateTrainUser(TrainUserConverter.toPO(trainUserBO));
    }

    @Override
    public int updateTrainUserReleaseFlag(TrainUserBO trainUserBO) {
        return trainUserDao.updateTrainUserReleaseFlag(TrainUserConverter.toPO(trainUserBO));
    }

    @Override
    public int deleteTrainUser(Long trainUserId) {
        return trainUserDao.deleteTrainUser(trainUserId);
    }

    @Override
    public TrainUserBO getTrainUser(Long trainUserId) {
        return TrainUserConverter.fromPO(trainUserDao.getTrainUser(trainUserId));
    }

    @Override
    public List<TrainUserBO> listTrainUserById(Collection<Long> collection) {
        return TrainUserConverter.fromPO(trainUserDao.listTrainUserById(collection));
    }

    @Override
    public List<TrainUserBO> listTrainUser(TrainUserQuery query) {
        List<TrainUserBO> trainUserBOS = TrainUserConverter.fromPO(trainUserDao.listTrainUser(query));
        build(trainUserBOS, query.getWithLearnRecords(), query.getWithExaminationRecords(), query.getWithPracticeRecords());
        return trainUserBOS;
    }

    @Override
    public List<TrainUserBO> listTrainUserFilterByUserId(TrainUserQuery query) {
        List<TrainUserBO> trainUserBOS = TrainUserConverter.fromPO(trainUserDao.listTrainUser(query));
        //根据人员去重
        trainUserBOS = trainUserBOS.stream().filter(distinctByKey(TrainUserBO::getUserId)).collect(Collectors.toList());
        build(trainUserBOS, query.getWithLearnRecords(), query.getWithExaminationRecords(), query.getWithPracticeRecords());
        return trainUserBOS;
    }

    private static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
        Map<Object, Boolean> seen = new ConcurrentHashMap<>();
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }

    private void build(List<TrainUserBO> trainUserBOS, boolean isWithLearnRecords, boolean isWithExaminationRecords, boolean isWithPracticeRecords) {
        if (CollectionUtils.isEmpty(trainUserBOS)) {
            return;
        }
        Long trainId = trainUserBOS.get(0).getTrainId();
        Set<Long> userIds = trainUserBOS.stream().map(TrainUserBO::getUserId).collect(Collectors.toSet());
        Map<Long, List<LearnRecordBO>> learnMap = new HashMap<>();
        Map<Long, List<ExaminationRecordBO>> examinationMap = new HashMap<>();
        Map<Long, List<PracticeRecordBO>> practiceMap = new HashMap<>();
        Map<Long, TrainDocumentRelBO> trainDocumentRelBOMap = new HashMap<>();
        if (isWithLearnRecords) {
            trainDocumentRelBOMap.putAll(TrainDocumentRelConverter.fromPO(trainDocumentRelDao.listTrainDocumentRel(TrainDocumentRelQuery.max().trainId(trainId).build())).stream().collect(Collectors.toMap(TrainDocumentRelBO::getDocumentId, t -> t)));
            //查询自学记录
            learnMap.putAll(LearnRecordConverter.fromPO(learnRecordDao.listLearnRecord(LearnRecordQuery.max().trainId(trainId).learnUserIds(userIds).build())).stream().collect(Collectors.groupingBy(LearnRecordBO::getLearnUserId)));
        }
        if (isWithExaminationRecords) {
            //查询考核记录
            examinationMap.putAll(ExaminationRecordConverter.fromPO(examinationRecordDao.listExaminationRecord(ExaminationRecordQuery.max().trainId(trainId).examineeUserIds(userIds).build())).stream().collect(Collectors.groupingBy(ExaminationRecordBO::getExamineeUserId)));
        }
        if (isWithPracticeRecords) {
            //查询实操记录
            practiceMap.putAll(PracticeRecordConverter.fromPO(practiceRecordDao.listPracticeRecord(PracticeRecordQuery.max().trainId(trainId).practiceUserIds(userIds).build())).stream().collect(Collectors.groupingBy(PracticeRecordBO::getPracticeUserId)));
        }
        if (!CollectionUtils.isEmpty(trainDocumentRelBOMap)) {
            documentClient.buildByDocument(trainDocumentRelBOMap.values(),
                    trainDocumentRelBOMap.values().stream().map(TrainDocumentRelBO::getDocumentId).collect(Collectors.toSet()),
                    TrainDocumentRelBO::getDocumentId,
                    (train, document) -> train.setDocumentName(document.getDocumentName()));
        }
        trainUserBOS.forEach(e -> {
            List<TrainDocumentRelBO> trainDocumentRelBOList = new ArrayList<>();
            if(learnMap.containsKey(e.getUserId())){
                learnMap.get(e.getUserId()).forEach(l -> {
                    if (trainDocumentRelBOMap.containsKey(l.getLessonId())) {
                        TrainDocumentRelBO trainDocumentRelBO = trainDocumentRelBOMap.get(l.getLessonId());
                        trainDocumentRelBO.setLearnDuration((l.getLearnDuration() + (trainDocumentRelBO.getLearnDuration() == null ? 0 : trainDocumentRelBO.getLearnDuration())));
                        if (trainDocumentRelBO.getLearnDuration() != null && trainDocumentRelBO.getLearnDuration() != 0) {
                            trainDocumentRelBO.setLearnDurationString(DateUtils.formatDateTime(trainDocumentRelBO.getLearnDuration()));
                            trainDocumentRelBO.setIsLearning(true);
                        } else {
                            trainDocumentRelBO.setIsLearning(false);
                        }
                        trainDocumentRelBOList.add(trainDocumentRelBO);
                    }
                });

                if (!CollectionUtils.isEmpty(trainDocumentRelBOList)) {
                    List<TrainDocumentRelBO> collect = trainDocumentRelBOList.stream().filter(TrainDocumentRelBO::getIsLearning).collect(Collectors.toList());
                    long totalTime = trainDocumentRelBOList.stream().mapToLong(TrainDocumentRelBO::getLearnDuration).sum();
                    e.setLearnDurationString(DateUtils.formatDateTime(totalTime));
                    e.setIsLearning(!CollectionUtils.isEmpty(collect));
                } else {
                    e.setIsLearning(false);
                }
                e.setTrainDocumentRelBOS(trainDocumentRelBOList);
            }
            if (examinationMap.containsKey(e.getUserId())) {
                List<ExaminationRecordBO> examinationRecordBOList = examinationMap.get(e.getUserId());
                if (!CollectionUtils.isEmpty(examinationRecordBOList)) {
                    examinationRecordBOList.sort((m1, m2) -> m2.getScore().compareTo(m1.getScore()));
                    ExaminationRecordBO examinationRecordBO = examinationRecordBOList.get(0);
                    e.setExaminationPaperName(examinationRecordBO.getExaminationPaperName());
                    e.setScore(examinationRecordBO.getScore());
                    e.setExaminationTime(examinationRecordBO.getExamineeTime());
                    e.setPassFlag(examinationRecordBO.getPassFlag());
                } else {
                    e.setPassFlag(3);
                }
                e.setExaminationRecordBOS(examinationRecordBOList);
            }
            if (practiceMap.containsKey(e.getUserId())) {
                List<PracticeRecordBO> practiceRecordBOS = practiceMap.get(e.getUserId());
                if (!CollectionUtils.isEmpty(practiceRecordBOS)) {
                    practiceRecordBOS.sort((m1, m2) -> m2.getCreateTime().compareTo(m1.getCreateTime()));
                    PracticeRecordBO practiceRecordBO = practiceRecordBOS.get(0);
                    e.setPracticeCreateTime(practiceRecordBO.getCreateTime());
                    e.setPracticePassFlag(practiceRecordBO.getPassFlag());
                } else {
                    e.setPracticePassFlag(3);
                }
                e.setPracticeRecordBOS(practiceRecordBOS);
            }
        });
    }

    @Override
    public int countTrainUser(TrainUserQuery query) {
        return trainUserDao.countTrainUser(query);
    }

    @Override
    public void deleteTrainUserByTrainId(Long trainId) {
        trainUserDao.deleteTrainUserByTrainId(trainId);
    }

}
